﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;

namespace cs2335lab5
{
    namespace Client
    {
        /// <summary>
        /// Interaction logic for ClientForm.xaml
        /// </summary>
        public partial class ClientForm : Window
        {
            /// <summary>
            /// This is basically a shortcut to reference the user data in the App class
            /// </summary>
            private Data.User user
            {
                get
                {
                    return App.Instance().User;
                }
                set
                {
                    App.Instance().User = value;
                }
            }

            /// <summary>
            /// Accessor for the user list on the server
            /// </summary>
            public Dictionary<string, Data.User> Users 
            {
                get
                {
                    lock (App.Instance().Proxy)
                    {
                        return App.Instance().Proxy.GetUserList();
                    }
                }
            }

            /// <summary>
            /// Accessor for the issue list on the server
            /// </summary>
            public Dictionary<Int64?, Data.Issue> Issues
            {
                get
                {
                    lock (App.Instance().Proxy)
                    {
                        return App.Instance().Proxy.GetIssueList();
                    }
                }
            }

            /// <summary>
            /// Public constructor. Calls Reset(), which populates the UI
            /// from data on the server
            /// </summary>
            public ClientForm()
            {
                InitializeComponent();

                // add an event handler for double-clicking on the ListView
                this.issuesListView.MouseDoubleClick += new MouseButtonEventHandler(issuesListView_MouseDoubleClick);
                this.usersListView.MouseDoubleClick += new MouseButtonEventHandler(usersListView_MouseDoubleClick);

                // set up bindings
                SetupBindings();

                // update the dialog data
                Reset();
            }

            /// <summary>
            /// Sets up command binding (UI menu items)
            /// some code from http://nayyeri.net/blog/Commands-in-Avalon/
            /// </summary>
            private void SetupBindings()
            {
                // set up some command bindings
                // VS doesn't like the CanExecute handlers for some reason?
                /* 
                CommandBinding binding = new CommandBinding();
                binding.Command = ApplicationCommands.Cut;
                binding.Executed += new ExecutedRoutedEventHandler(Cut_Execute);
                binding.CanExecute += new CanExecuteRoutedEventHandler(Cut_CanExecute);
                this.CommandBindings.Add(binding);

                binding = new CommandBinding();
                binding.Command = ApplicationCommands.Copy;
                binding.Executed += new ExecutedRoutedEventHandler(Copy_Execute);
                binding.CanExecute += new ExecutedRoutedEventHandler(Copy_CanExecute);
                this.CommandBindings.Add(binding);

                binding = new CommandBinding();
                binding.Command = ApplicationCommands.Paste;
                binding.Executed += new ExecutedRoutedEventHandler(Paste_Execute);
                binding.CanExecute += new ExecutedRoutedEventHandler(Paste_CanExecute);
                this.CommandBindings.Add(binding);

                binding = new CommandBinding();
                binding.Command = ApplicationCommands.Delete;
                binding.Executed += new ExecutedRoutedEventHandler(Delete_Execute);
                binding.CanExecute += new ExecutedRoutedEventHandler(Delete_CanExecute);
                this.CommandBindings.Add(binding);

                binding = new CommandBinding();
                binding.Command = ApplicationCommands.SelectAll;
                binding.Executed += new ExecutedRoutedEventHandler(SelectAll_Execute);
                binding.CanExecute += new ExecutedRoutedEventHandler(SelectAll_CanExecute);
                this.CommandBindings.Add(binding);
                 * */
            }

            /// <summary>
            /// Refreshing the form is actually just resetting it, which calls 
            /// ResetIssues() and ResetUsers().
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            void Refresh_Click(object sender, RoutedEventArgs e)
            {
                Reset();
            }

            private void usersListView_MouseDoubleClick(object sender, MouseButtonEventArgs e)
            {
                // get the source of the click
                DependencyObject dep = (DependencyObject)e.OriginalSource;

                // get its parent
                while ((dep != null) && !(dep is ListViewItem))
                {
                    dep = VisualTreeHelper.GetParent(dep);
                }

                // if it exists...
                if (dep == null)
                {
                    return;
                }

                // get the data row from the list view
                System.Data.DataRowView drv = (System.Data.DataRowView)usersListView.ItemContainerGenerator.ItemFromContainer(dep);
                System.Data.DataRow dr = drv.Row;

                // now that we have the row, we can open the data item
                // get the item's ID
                string id = (string)drv.Row[0];
                // pass it to the app
                App.Instance().ShowUserDetail(id);
            }

            private void issuesListView_MouseDoubleClick(object sender, MouseButtonEventArgs e)
            {
                DependencyObject dep = (DependencyObject)e.OriginalSource;

                while ((dep != null) && !(dep is ListViewItem))
                {
                    dep = VisualTreeHelper.GetParent(dep);
                }

                if (dep == null)
                    return;

                System.Data.DataRowView drv = (System.Data.DataRowView)issuesListView.ItemContainerGenerator.ItemFromContainer(dep);
                System.Data.DataRow dr = drv.Row;

                // Now that we have the row, we can open the data item
                // get the item's ID
                Int64? id = (Int64?)Convert.ToInt64(drv.Row[1]);
                // pass it to the app
                App.Instance().ShowIssueDetail(id);
            }

            /// <summary>
            /// Exit application when this window is closed
            /// </summary>
            /// <param name="sender">sender</param>
            /// <param name="e">event args</param>
            private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
            {
                // log off
                App.Instance().Proxy.LogOut(user);

                // exit application
                Application.Current.Shutdown();
            }

            /// <summary>
            /// Resets the form back to its original state
            /// Use this to repopulate the form on login
            /// </summary>
            public void Reset()
            {
                ResetIssues();
                ResetUsers();
            }

            /// <summary>
            /// Function that gets the Issues dictionary from the network and puts it into the GUI
            /// </summary>
            public void ResetIssues()
            {
                System.Data.DataTable dt = new System.Data.DataTable("Issues");
                dt.Columns.Add("Name", typeof(string));
                dt.Columns.Add("ID", typeof(string));
                dt.Columns.Add("Detailed description", typeof(string));
                dt.Columns.Add("Creation date", typeof(string));
                dt.Columns.Add("Due date", typeof(string));
                dt.Columns.Add("Assigned to", typeof(string));
                dt.Columns.Add("Status", typeof(string));
                dt.Columns.Add("Hours worked", typeof(string));
                dt.Columns.Add("Hours remaining", typeof(string));

                // loop through all of the issues and add them to the DataTable
                Data.Issue i = null;
                foreach (KeyValuePair<Int64?, Data.Issue> issuesLine in Issues)
                {
                    i = (Data.Issue)issuesLine.Value;
                    dt.Rows.Add(i.Name, 
                        i.ID.ToString(), 
                        i.DetailedDescription, 
                        (i.CreationDate == DateTime.MinValue) ? "" : i.CreationDate.ToShortDateString() + " " + i.CreationDate.ToShortTimeString(),
                        (i.DueDate == DateTime.MinValue) ? "" : i.DueDate.ToShortDateString() + " " + i.DueDate.ToShortTimeString(), 
                        (i.AssignedTo == null || i.AssignedTo.Name == null) ? "" : i.AssignedTo.Name, 
                        (i.IssueStatus == null) ? "" : Enum.GetName(typeof(Data.Issue.Status), i.IssueStatus),
                        i.HoursWorked.ToString(), 
                        i.HoursToGo.ToString()
                    );
                }

                // set the DataTable as the ListView's current DataContext (bind to it)
                //issuesListView.DataContext = dt;

                //create two dataviews, 1 will be filtered and 2 will not be
                System.Data.DataView dataView1 = new System.Data.DataView(dt);
                System.Data.DataView dataView2 = new System.Data.DataView(dt);

                if ((bool)this.filterBox.IsChecked)
                {
                    //use the filtered view
                    this.issuesListView.ItemsSource = dataView1;
                }
                else
                {
                    //use the unfiltered one
                    this.issuesListView.ItemsSource = dataView2;
                }

                //filter dataView 1 to only display issues assigned
                //to the current user
                StringBuilder sb = new StringBuilder();
                sb.Append("[Assigned to] = '");
                sb.Append(App.Instance().User.Name);
                sb.Append("'");
                dataView1.RowFilter = sb.ToString();
            }

            /// <summary>
            /// Function that gets the users dictionary from the network and puts it into the GUI
            /// </summary>
            public void ResetUsers()
            {
                System.Data.DataTable dt = new System.Data.DataTable("Users");
                dt.Columns.Add("ID", typeof(string));
                dt.Columns.Add("Name", typeof(string));
                dt.Columns.Add("Access", typeof(string));
                dt.Columns.Add("Email", typeof(string));
                dt.Columns.Add("Online", typeof(string));

                // loop through all of the users and add them to the DataTable
                Data.User u = null;
                foreach (KeyValuePair<string, Data.User> usersLine in Users)
                {
                    u = (Data.User)usersLine.Value;
                    dt.Rows.Add(u.ID, 
                        u.Name, 
                        Enum.GetName(typeof(Data.User.Privileges), u.Access), 
                        u.Email,
                        u.isLoggedIn ? "Yes" : "No"
                    );
                }

                // set the DataTable as the ListView's current DataContext (bind to it)
                usersListView.DataContext = dt;
            }

            /// <summary>
            /// Event handler for the 'Log out' button
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void LogOut_Click(object sender, RoutedEventArgs e)
            {
                try
                {
                    App.Instance().Proxy.LogOut(user);
                }
                catch (System.ServiceModel.EndpointNotFoundException)
                {
                    // we're disconnecting anyway, we don't really need 
                    // to do anything here.

                    // (chances are, the server was closed)
                }

                App.Instance().ShowLogin();
            }

            /// <summary>
            /// Used to track the list view column currently being sorted
            /// </summary>
            private GridViewColumnHeader _CurSortCol = null;
            private SortAdorner _CurAdorner = null;

            /// <summary>
            /// Called when you click on a column header.
            /// this code is from http://www.switchonthecode.com/tutorials/wpf-tutorial-using-the-listview-part-2-sorting
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void Sort_Click(object sender, RoutedEventArgs e, ListView lv)
            {
                GridViewColumnHeader column = sender as GridViewColumnHeader;
                string field = column.Tag as string;

                if (_CurSortCol != null)
                {
                    AdornerLayer.GetAdornerLayer(_CurSortCol).Remove(_CurAdorner);
                    lv.Items.SortDescriptions.Clear();
                }

                ListSortDirection newDir = ListSortDirection.Ascending;
                if (_CurSortCol == column && _CurAdorner.Direction == newDir)
                {
                    newDir = ListSortDirection.Descending;
                }

                _CurSortCol = column;
                _CurAdorner = new SortAdorner(_CurSortCol, newDir);
                AdornerLayer.GetAdornerLayer(_CurSortCol).Add(_CurAdorner);
                lv.Items.SortDescriptions.Add(new SortDescription(field, newDir));
            }

            /// <summary>
            /// Sort the issues list view
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void IssueSort_Click(object sender, RoutedEventArgs e)
            {
                Sort_Click(sender, e, issuesListView);
            }

            /// <summary>
            /// Sort the users list view
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void UserSort_Click(object sender, RoutedEventArgs e)
            {
                Sort_Click(sender, e, usersListView);
            }

            /// <summary>
            /// Delete the selected issues
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void DeleteSelectedIssue_Click(object sender, RoutedEventArgs e)
            {
                // remove selected rows from the item source
                System.Data.DataView dv = (issuesListView.ItemsSource as System.Data.DataView);
                System.Data.DataTable dt = (dv.Table as System.Data.DataTable);
                Int64? index = null;
                try
                {
                    foreach (System.Data.DataRowView rowview in issuesListView.SelectedItems)
                    {
                        System.Data.DataRow row = rowview.Row;
                        // get the index and tell the server to delete this issue
                        index = (Int64?)System.Convert.ToInt64(row.ItemArray[1]);
                        App.Instance().Proxy.DeleteIssue(index);

                        // remove the row from the local data store
                        dt.Rows.Remove(row);
                    }
                }
                catch (InvalidOperationException)
                {
                    // try again?
                    DeleteSelectedIssue_Click(sender, e);
                }
            }

            /// <summary>
            /// Delete the selected users
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void DeleteSelectedUser_Click(object sender, RoutedEventArgs e)
            {
                // remove selected rows from the item source
                System.Data.DataView dv = (usersListView.ItemsSource as System.Data.DataView);
                System.Data.DataTable dt = (dv.Table as System.Data.DataTable);
                string index = null;
                try
                {
                    foreach (System.Data.DataRowView rowview in usersListView.SelectedItems)
                    {
                        System.Data.DataRow row = rowview.Row;
                        // get the index and tell the server to delete this issue
                        index = Convert.ToString(row.ItemArray[0]);
                        App.Instance().Proxy.DeleteUser(index);

                        // remove the row from the local data store
                        dt.Rows.Remove(row);
                    }
                }
                catch (InvalidOperationException)
                {
                    // try again?
                    DeleteSelectedUser_Click(sender, e);
                }
            }

            /// <summary>
            /// Either add a blank issue inline and let them edit it,
            /// or pop up a pretty dialog box with fields and such
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void NewIssue_Click(object sender, RoutedEventArgs e)
            {
                App.Instance().ShowIssueDetail(null);
            }

            /// <summary>
            /// Register a new user
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void NewUser_Click(object sender, RoutedEventArgs e)
            {
                App.Instance().ShowUserDetail(null);
            }

            /// <summary>
            /// Edit the selected issue
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void EditIssue_Click(object sender, RoutedEventArgs e)
            {
                if (issuesListView.SelectedItem != null)
                {
                    // get the ID of the selected issue
                    System.Data.DataRowView rowview = (System.Data.DataRowView)issuesListView.SelectedItem;
                    System.Data.DataRow row = rowview.Row;
                    Int64? index = Convert.ToInt64(row.ItemArray[1]);

                    // show it
                    App.Instance().ShowIssueDetail(index);
                }
            }

            /// <summary>
            /// Edit the selected issue
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void EditUser_Click(object sender, RoutedEventArgs e)
            {
                if (usersListView.SelectedItem != null)
                {
                    // get the ID of the selected issue
                    System.Data.DataRowView rowview = (System.Data.DataRowView)issuesListView.SelectedItem;
                    System.Data.DataRow row = rowview.Row;
                    string index = Convert.ToString(row.ItemArray[0]);

                    // show it
                    App.Instance().ShowUserDetail(index);
                }
            }

            /// <summary>
            /// Handler for file-->exit menu option
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void Exit_Click(object sender, RoutedEventArgs e)
            {
                Window_Closing(sender, null);
            }

            /// <summary>
            /// Handle a click on the checkbox
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void Filter_Click(object sender, RoutedEventArgs e)
            {
                ResetIssues();
            }

            /// <summary>
            /// Select all of the items in the issues list view
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void SelectAllIssues_Click(object sender, RoutedEventArgs e)
            {
                IssuesTab.Focus();
                issuesListView.SelectAll();
            }

            /// <summary>
            /// Select all of the users in the users list view
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void SelectAllUsers_Click(object sender, RoutedEventArgs e)
            {
                UsersTab.Focus();
                usersListView.SelectAll();
            }
        }
    }
}